﻿using System;
using System.Collections.Generic;
using System.Linq;
using BlueCrest.ConfigManagement.Core.Data;
using BlueCrest.ConfigManagement.Storage.DB.Data;
using BlueCrest.ConfigManagement.Storage.DB.Exceptions;
using NHibernate;
using NHibernate.Linq;
using ConfigPathEntryEntity = BlueCrest.ConfigManagement.Core.Data.ConfigPathEntryEntity;

namespace BlueCrest.ConfigManagement.Storage.DB.Service
{
    /// <summary>
    /// Implementation of the <see cref="IConfigStorageService"/> via DB data store
    /// </summary>
    public class DbConfigStorageService : IConfigStorageService
    {
        #region Field and Props

        private readonly INHibernateSessionFactory _sessionFactory;
        private readonly IConfigPathAdapterService _configPathAdapterService;

        private ISession _session;
        public ISession Session
        {
            get
            {
                if (_session == null)
                {
                    _session = _sessionFactory.CreateSessionFactory().OpenSession();
                }

                return _session;
            }
        } 

        #endregion

        #region Ctor

        public DbConfigStorageService(INHibernateSessionFactory sessionFactory, IConfigPathAdapterService configPathAdapterService)
        {
            _sessionFactory = sessionFactory;
            _configPathAdapterService = configPathAdapterService;
        }

        #endregion

        public List<ScopeEntity> GetScopes()
        {
            return Session.Query<ScopeEntity>().ToList();
        }

        public List<ConfigPathTreeEntity> GetAllConfigPath()
        {
            var flatTree = Session.Query<ConfigPathFlatEntity>().ToList();

            return _configPathAdapterService.Adapt(flatTree).ToList();
        }

        public List<ConfigPathTreeEntity> GetConfigPathStartingFrom(int pathId, int treeDepth = -1)
        {
            var parentTree = Session.Query<ConfigPathFlatEntity>().FirstOrDefault(x => x.PathId == pathId);

            if(parentTree == null)
            {
                throw new ParentConfigPathIsNotFoundException();
            }

            var children = BuildChildrenTree(parentTree, 0, treeDepth).ToList();
            
            return _configPathAdapterService.Adapt(children, parentTree).ToList();
        }

        /// <summary>
        /// Gets the Entries for the given ConfigPathId
        /// </summary>
        /// <param name="pathId"></param>
        /// <returns></returns>
        public List<ConfigPathEntryEntity> GetEntries(int pathId)
        {
            var pathItem = Session.Query<ConfigPathFlatEntity>().FirstOrDefault(x => x.PathId == pathId);

            if(pathItem == null)
            {
                throw new ConfigPathIsNotFoundException(pathId);
            }

            var entries = Session.Query<DB.Data.ConfigPathEntryEntity>()
                    .Where(x => x.EntryPathId == pathId)
                    .ToList();

            var scopes = GetScopes();

            var adaptedEntries = _configPathAdapterService.Adapt(scopes, entries);

            return adaptedEntries.ToList();
        }

        private IEnumerable<ConfigPathFlatEntity> BuildChildrenTree(ConfigPathFlatEntity currentItem, int currentIter, int totalIter = -1)
        {
            var children = Session.Query<ConfigPathFlatEntity>().Where(x => x.ParentPathId == currentItem.PathId).ToList();

            //increment tree size
            currentIter++;
            foreach (var child in children)
            {
                yield return child;

                if(totalIter == -1 || currentIter < totalIter)
                {
                    //if current iteration is within TotalIter when look at its children as well
                    foreach (var grandChild in BuildChildrenTree(child, currentIter, totalIter))
                    {
                        yield return grandChild;
                    }
                }
            }
        }
    }
}
